﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.IO;
using System.Text;

namespace IMIForUnity
{
    public class ImiWrapper
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        private const string filePath = "IminectUnity";
        private const string imirenderer = "imirenderer";

#else
        private const string filePath = "ImiUnitySDK";
#endif

        public const int MAX_TRACKED_PEOPLE_NUM = 6;

        public enum ImiDeviceState
        {
            IMI_DEVICE_STATE_CONNECT 	= 0,
            IMI_DEVICE_STATE_DISCONNECT = 1,
        }

        public struct ImiVector4
        {
            public float x;
            public float y;
            public float z;
            public float w;
        }


        /// <summary>
        /// Types of frame provided by devices
        /// The device can only transfer two frames at most, one is color frame,
        /// the other is one kind in the four: depth, skeleton, depth and skeleton, user depth and skeleton
        /// </summary>
        public enum ImiFrameType
        {
            //Provide Depth Frame Only, Depth Frame is 640*480*2 B
            IMI_DEPTH_FRAME = 0x00,

            //Provide Skeleton Frame Only, Skeleton Frame is transported in struct
            IMI_SKELETON_FRAME = 0x03,

            //Provide Both Depth Frame and Skeleton Frame, which equals depth fram + skeleton frame
            IMI_DEPTH_SKELETON_FRAME = 0x01,//320*240 only

            //Provide Depth Frame with User Index And Skeleton Data
            //This frame is different from IMI_DEPTH_SKELETON_FRAME only that in this frame,
            //fir 3 bit of depth data is used for user index
            //This frame contains both depth frame and skeleton data
            IMI_USER_INDEX_SKELETON_FRAME = 0x02,//320*240 only

            //Provide Color Frame, Independent from the other four frames
            IMI_COLOR_FRAME = 0x04
        }

        public enum ImiPixelFormat
        {
            IMI_PIXEL_FORMAT_DEP_16BIT = 0x00, //Depth
            IMI_PIXEL_FORMAT_IMAGE_YUV422 = 0x01, //Rgb
            IMI_PIXEL_FORMAT_IMAGE_H264 = 0x02, //H264 Compressed
            IMI_PIXEL_FORMAT_IMAGE_RGB24 = 0x03,
            IMI_PIXEL_FORMAT_IMAGE_YUV420SP = 0x04, //NV21
        }

        public struct ImiSkeletonData
        {
            public ImiSkeletonTrackingState trackingState;
            public uint trackingID;
            public uint enrollIndex;
            public uint usrIndex;
            //Player Position
            public ImiVector4 position;

            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 20, ArraySubType = UnmanagedType.Struct)]
            public ImiVector4[] skeletonPositions;

            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 20, ArraySubType = UnmanagedType.Struct)]
            public ImiSkeletonPositionTrackingState[] skeletonPositionTrackingStates;

            public uint   qualityFlags;
        }

        public struct ImiSkeletonFrame
        {
            //Floor where users stand
            public ImiVector4 floorClipPlane;

            //Skeleton Data, maxium users 2
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.Struct)]
            public ImiSkeletonData[] skeletonData;
        }

        public struct ImiSkeletonJointOrientation
        {
            public ImiSkeleton.Index endJoint;
            public ImiSkeleton.Index startJoint;
            public IMI_SKELETON_BONE_ROTATION hierarchicalRotation;
            public IMI_SKELETON_BONE_ROTATION absoluteRotation;
        }

        public struct IMI_SKELETON_BONE_ROTATION
        {
            public ImiMatrix4 rotationMatrix;
            public ImiVector4 rotationQuaternion;
        }

        public struct ImiMatrix4
        {
            public float M11;
            public float M12;
            public float M13;
            public float M14;
            public float M21;
            public float M22;
            public float M23;
            public float M24;
            public float M31;
            public float M32;
            public float M33;
            public float M34;
            public float M41;
            public float M42;
            public float M43;
            public float M44;
        }

        public enum IMI_UPGRADE_PROMPT{
            IMI_UPGRADE_START = 0,
            IMI_UPLOADBLOCK_START = 1,
            IMI_UPLOADBLOCK_UPLOADING = 2,
            IMI_UPLOADBLOCK_PASS = 3,
            IMI_UPLOADBLOCK_FAIL = 4,
            IMI_REBOOT_START = 5,
            IMI_REBOOTING = 6,
            IMI_REBOOT_PASS = 7,
            IMI_REBOOT_FAIL = 8,
            IMI_CHCKING_START = 9,
            IMI_CHCKING_PASS = 10,
            IMI_CHCKING_FAIL = 11,
            IMI_UPGRADE_PASS = 12,
            IMI_UPGRADE_FAIL = 13,
        }


        /// <summary>
        /// 打开设备并初始化,适用于所有平台，以及root过了的Android平台
        /// </summary>
        /// <returns>
        /// 0：成功.
        /// 1：设备初始化失败.
        /// 2：设备未找到.
        /// 3：打开失败.
        /// </returns>
        [DllImport(filePath, EntryPoint = "OpenDevice")]
        public static extern ErrorCode OpenDevice();


        /// <summary>
        /// 功能同OpenDevice，专门为Android平台所提供的接口，在获取读写Usb权限后，根据Android返回的fd打开设备
        /// </summary>
        /// <param name="fd">File Descriptor</param>
        /// <param name="path">Usb Device Absolute Path</param>
        /// <returns>同OpenDevice()</returns>
        [DllImport(filePath, EntryPoint = "OpenDevice2")]
        public static extern ErrorCode OpenDevice2(int fd, string path);

        /// <summary>
        /// 关闭设备.
        /// </summary>
        [DllImport(filePath, EntryPoint = "CloseDevice")]
        public static extern ErrorCode CloseDevice();

        /// <summary>
        /// 打开数据流.
        /// </summary>
        /// <param name="frameType">数据帧枚举类型.</param>
        /// <param name="imageResolution"></param>
        /// <param name="pixelFormat"></param>
        /// <returns></returns>
        [DllImport(filePath, EntryPoint = "OpenStream")]
        public static extern ErrorCode OpenStream(ImiFrameType frameType, ImiResolution.Type imageResolution, ImiPixelFormat pixelFormat);

        [DllImport(filePath, EntryPoint = "CloseStream")]
        public static extern ErrorCode CloseStream(ImiFrameType frameType);

        /// <summary>
        /// 获取彩色数据.
        /// 每调用一次，消耗一帧彩色图
        /// </summary>
        /// <param name="colorData">彩色数据（kinect存储类型为BGRA）.</param>
        /// <param name="timeOut">等待时间.</param>
        /// <returns>
        /// 0：成功.
        /// 1：失败.
        /// </returns>
        [DllImport(filePath, EntryPoint = "GetColorData")]
        public static extern ErrorCode GetColorData(byte[] colorData, uint timeOut);


        /// <summary>
        /// 获取深度原始数据
        /// 每调用一次，消耗一帧深度图和骨架
        /// </summary>
        /// <param name="depthData"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        [DllImport(filePath, EntryPoint = "GetDepthData")]
        public static extern ErrorCode GetDepthData(ushort[] depthData, uint timeOut);

        [DllImport(filePath, EntryPoint = "GetColorAndDepthData")]
        public static extern ErrorCode GetColorAndDepthData(byte[] colorDataRaw, ushort[] depthDataRaw, ref ImiSkeletonFrame skeletonFrame, uint timeout);

        [DllImport(filePath, EntryPoint = "GetDepthTextureData")]
        public static extern ErrorCode GetDepthTextureData(
            byte[] depthTexData, 
            ushort[] depthData, 
            uint timeout,
            bool needRaw);


        /// <summary>
        /// 获取深度原始数据和骨骼原始数据
        /// 每调用一次，消耗一帧深度图和骨架
        /// </summary>
        /// <param name="depthData"></param>
        /// <param name="skeletonFrame"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        [DllImport(filePath, EntryPoint = "GetDepthDataAndSkeletonFrame")]
        public static extern ErrorCode GetDepthDataAndSkeletonFrame(ushort[] depthData, ref ImiSkeletonFrame skeletonFrame, uint timeOut);

        [DllImport(filePath, EntryPoint = "GetDepthTexDataAndSkeletonFrame")]
        public static extern ErrorCode GetDepthTexDataAndSkeletonFrame(
            byte[] depthTexData, 
            ushort[] depthData, 
            ref ImiSkeletonFrame skeletonFrame, 
            uint timeout,
            bool needRaw);

        /// <summary>
        /// 获取骨骼原始数据
        /// 每调用一次，消耗一帧骨架
        /// </summary>
        /// <param name="skeletonFrame"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        [DllImport(filePath, EntryPoint = "GetSkeletonFrame")]
        public static extern ErrorCode GetSkeletonFrame(ref ImiSkeletonFrame skeletonFrame, uint timeOut);

        /// <summary>
        /// 获取深度前景图原始数据和骨骼原始数据
        /// 每调用一次，消耗一帧深度图和一帧骨骼
        /// </summary>
        /// <param name="depthPlayerData"></param>
        /// <param name="skeletonFrame"></param>
        /// <param name="timeOut"></param>
        /// <returns></returns>
        [DllImport(filePath, EntryPoint = "GetDepthPlayerDataAndSkeletonFrame")]
        public static extern ErrorCode GetDepthPlayerDataAndSkeletonFrame(ushort[] depthPlayerData, ref ImiSkeletonFrame skeletonFrame, uint timeOut);

        [DllImport(filePath, EntryPoint = "GetDepthPlayerTexDataAndSkeletonFrame")]
        public static extern ErrorCode GetDepthPlayerTexDataAndSkeletonFrame(
            byte[] depthTexData, 
            ushort[] depthPlayerData, 
            ref ImiSkeletonFrame skeletonFrame, 
            uint timeout, 
            bool needRaw);



        /// <summary>
        /// 获得指定玩家的深度抠图, 如果userId为0，就返回所有玩家的深度图, 不需要则 userId = -1 
        /// 最多支持2个玩家
        /// 每调用一次，消耗一帧深度图和一帧骨骼图
        /// </summary>
        /// <param name="depthTexData"></param>
        /// <param name="skeletonFrame"></param>
        /// <param name="userId"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        [DllImport(filePath, EntryPoint = "GetDepthPlayerTextureData")]
        public static extern ErrorCode GetDepthPlayerTextureData(
            byte[] depthTexData0, 
            int userId0, 
            byte[] depthTexData1, 
            int userId1, 
            ushort[] depthDataRaw,
            ref ImiSkeletonFrame skeletonFrame, 
            uint timeout,
            bool needRaw);


        /// <summary>
        /// 获得指定玩家的彩色抠图,如果userId为0，或者大于6，就返回一张空的图片, 不需要则 userId = -1
        /// 最多支持2个玩家
        /// 每调用一次，消耗一帧深度图，一帧彩色图，一帧骨骼
        /// </summary>
        /// <param name="colorTexData"></param>
        /// <param name="skeletonFrame"></param>
        /// <param name="userId"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        [DllImport(filePath, EntryPoint = "GetColorPlayerTextureData")]
        public static extern ErrorCode GetColorPlayerTextureData(
            byte[] colorTexData0,
            int userId0, 
            byte[] colorTexData1,
            int userId1, 
            byte[] colorDataRaw,
            ushort[] depthDataRaw,
            ref ImiSkeletonFrame skeletonFrame,
            uint timeout,
            bool needRaw);

        /// <summary>
        /// 获取玩家居中的彩色抠图，如果userId为0，或者大于6，就返回一张空的图片, 不需要则 userId = -1 
        /// 最多支持2个玩家
        /// 每调用一次，消耗一帧深度图，一帧彩色图，一帧骨骼
        /// </summary>
        /// <param name="depthTexData"></param>
        /// <param name="skeletonFrame"></param>
        /// <param name="userId"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        [DllImport(filePath, EntryPoint = "GetNormalizedColorPlayerTextureData")]
        public static extern ErrorCode GetNormalizedColorPlayerTextureData(
            byte[] colorTexData0,
            int userId0, 
            byte[] colorTexData1,
            int userId1, 
            byte[] colorDataRaw,
            ushort[] depthDataRaw,
            ref ImiSkeletonFrame skeletonFrame,
            uint timeout,
            bool needRaw);

        /// <summary>
        /// 是否开启骨架平滑.
        /// </summary>
        /// <param name="isEnable">true:开启 false:关闭</param>
        [DllImport(filePath, EntryPoint = "EnableSmoothSkeletonFrame")]
        public static extern void EnableSmoothSkeletonFrame(bool isEnable);

        /// <summary>
        /// 骨架坐标转换成深度坐标
        /// </summary>
        /// <param name="skeletonPosition">骨架坐标</param>
        /// <param name="depthX">深度X坐标</param>
        /// <param name="depthY">深度Y坐标</param>
        /// <param name="depthZ">深度值</param>
        /// <param name="depthReslution">深度图分辨率</param>
        /// <returns>0表示成功，1表示失败</returns>
        [DllImport(filePath, EntryPoint = "ConvertSkeletonPointToDepthPoint")]
        public static extern void ConvertSkeletonPointToDepthPoint(ImiVector4 skeletonPosition, ref int depthX, ref int depthY, ref int depthZ, ImiResolution.Type depthReslution);

        /// <summary>
        /// 深度坐标转换成彩色坐标
        /// </summary>
        /// <param name="depthX">深度X坐标</param>
        /// <param name="depthY">深度Y坐标</param>
        /// <param name="depthZ">深度值</param>
        /// <param name="colorX">彩色X坐标</param>
        /// <param name="colorY">彩色Y坐标</param>
        /// <param name="depthReslution">深度图分辨率</param>
        /// <param name="colorReslution">彩色图分辨率</param>
        /// <returns>0表示成功，1表示失败</returns>
        [DllImport(filePath, EntryPoint = "ConvertDepthPointToColorPoint")]
        public static extern void ConvertDepthPointToColorPoint(int depthX, int depthY, int depthZ, ref int colorX, ref int colorY, ImiResolution.Type depthReslution, ImiResolution.Type colorReslution);

        /// <summary>
        /// 深度坐标转换成骨架坐标
        /// </summary>
        /// <param name="depthX">深度X坐标</param>
        /// <param name="depthY">深度Y坐标</param>
        /// <param name="depthZ">深度值</param>
        /// <param name="skeletonPosition">骨架坐标</param>
        /// <param name="depthReslution">深度图分辨率</param>
        /// <returns>0表示成功，1表示失败</returns>
        [DllImport(filePath, EntryPoint = "ConvertDepthPointToSkeletonPoint")]
        public static extern void ConvertDepthPointToSkeletonPoint(int depthX, int depthY, int depthZ, ref ImiVector4 skeletonPosition, ImiResolution.Type depthReslution);

        [DllImport(filePath, EntryPoint = "GetDeviceState")]
        public static extern ImiDeviceState GetDeviceState();

        [DllImport(filePath, EntryPoint = "SetTrackingUserId")]
        public static extern ErrorCode SetTrackingUserId(int id, bool track);

        [DllImport(filePath, EntryPoint = "GetDeviceInfo")]
        public static extern ErrorCode GetDeviceInfo(ref ImiDeviceInfo device);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void OnUpgradeDownloaded();

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void OnDeviceUpgrading(ImiWrapper.IMI_UPGRADE_PROMPT promptId, float progress);

        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public delegate void UnityLog(string log);

        [DllImport(filePath, EntryPoint = "SetDeviceUpgradeCallback")]
        public static extern void  SetDeviceUpgradeCallback(System.IntPtr upgraded, System.IntPtr upgrading);

        [DllImport(filePath, EntryPoint = "SetUpgrade")]
        public static extern void SetUpgrade(bool shouldUpgrade);

        [DllImport(filePath, EntryPoint = "SetLogFunction")]
        public static extern void SetLogFunction(System.IntPtr logPtr);

#if UNITY_ANDROID && !UNITY_EDITOR
        [DllImport(filePath, EntryPoint = "HisiTVsetMemcEnable")]
        public static extern ErrorCode HisiTVsetMemcEnable(bool enable);

        [DllImport(filePath, EntryPoint = "LoadColorTexture")]
        public static extern void LoadColorTexture(ImiWrapper.ImiPixelFormat rendererFormat);

        [DllImport(imirenderer)]
        public static extern int Imi_Renderer_Surface(IntPtr texPtr, int texWidth, int texHeight);

        [DllImport(imirenderer)]
        public static extern int OVR_Media_Surface_SetEventBase(int eventId);
#endif

    }
}
